package nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Set;

/**
 * @author fengbo
 */
public class NioClient {

    public static void main(String[] args) throws IOException {
        // NioEventLoop类的构造方法中会调用本类的openSelector()方法，
        // 在该方法中会调用JDK底层类SelectorProvider.provider().openSelector()打开一个选择器。
        Selector selector = Selector.open();

        // Bootstrap#connect(String, int) -> Bootstrap#doResolveAndConnect(SocketAddress, SocketAddress)
        // -> AbstractBootstrap#initAndRegister() -> ChannelFactory#newChannel()
        SocketChannel socketChannel1 = SocketChannel.open();
        // AbstractNioChannel的构造方法中
        socketChannel1.configureBlocking(false);
        // SingleThreadEventLoop#register(Channel) -> AbstractChannel#register(EventLoop, ChannelPromise)
        // -> register0(ChannelPromise) -> doRegister() -> AbstractNioChannel#doRegister()
        socketChannel1.register(selector, SelectionKey.OP_READ);
        // Bootstrap#doResolveAndConnect(SocketAddress, SocketAddress)
        // -> doConnect(SocketAddress, SocketAddress, ChannelPromise)
        // -> AbstractChannelHandlerContext#connect(SocketAddress, SocketAddress, ChannelPromise)
        // -> AbstractChannelHandlerContext#invokeConnect(SocketAddress, SocketAddress, ChannelPromise)
        // -> DefaultChannelPipeline#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)
        // -> AbstractNioChannel#connect(SocketAddress, SocketAddress, ChannelPromise)
        // -> NioSocketChannel#doConnect(SocketChannel, SocketAddress) -> SocketUtils#connect(SocketChannel, SocketAddress)
        socketChannel1.connect(new InetSocketAddress("127.0.0.1", 8080));

        SocketChannel socketChannel2 = SocketChannel.open();
        socketChannel2.configureBlocking(false);
        socketChannel2.register(selector, SelectionKey.OP_READ);
        socketChannel2.connect(new InetSocketAddress("127.0.0.1", 8081));

        socketChannel1.finishConnect();
        socketChannel2.finishConnect();

        for (int i = 0; i < 100; i++) {
            String msg1 = "1 hello word! " + i;
            socketChannel1.write(ByteBuffer.wrap(msg1.getBytes()));

            String msg2 = "2 hello word! " + i;
            socketChannel2.write(ByteBuffer.wrap(msg2.getBytes()));

            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            for (SelectionKey key : selectionKeys) {
                SocketChannel channel = (SocketChannel) key.channel();
                ByteBuffer buff = ByteBuffer.allocate(1024);
                channel.read(buff);
                buff.flip();
                CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
                String resp = decoder.decode(buff).toString();
                System.out.println(resp);
            }
        }
    }
}
